﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using zijian666.Core.Abstractions;
using zijian666.SuperConvert.Core;
using zijian666.SuperConvert.Extensions;
using zijian666.SuperConvert.Interface;

namespace zijian666.SuperConvert.Convertor.Base
{
    [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
    internal class TraceConvertor<T> : ConvertorWrapper<T>, IConvertor<T>
    {
        private static readonly DiagnosticListener _trace = new DiagnosticListener("zijian666.SuperConvert");

        private IConvertContext _context = new InnerConvertContext();

        public TraceConvertor(IConvertor<T> convertor) : base(convertor)
        {

        }

        public uint Priority => InnerConvertor.Priority;

        public ConvertResult<T> Convert(IConvertContext context, object input)
        {
            if (!_trace.IsEnabled() || ConvertContext.Tracing)
            {
                var r = InnerConvertor.Convert(context, input);
                if (r.Exception != null)
                {
                    r.Exception.Data.Add("Convert", InnerConvertor);
                    r.Exception.Data.Add("InputValue", input);
                    r.Exception.Data.Add("OutputType", typeof(T));
                }
                return r;
            }

            var activity = new Activity(InnerConvertor.GetType().GetFriendlyName());
            try
            {
                ConvertContext.Tracing = true;
                _trace.StartActivity(activity, input);
                ConvertContext.Tracing = false;
                var result = InnerConvertor.Convert(context, input);
                ConvertContext.Tracing = true;
                var ex = result.Exception;
                if (ex is null)
                {
                    _trace.StopActivity(activity, result.Value);
                }
                else
                {
                    ex.Data.Add("Convert", InnerConvertor);
                    ex.Data.Add("InputValue", input);
                    ex.Data.Add("OutputType", typeof(T));
                    _trace.StopActivity(activity, ex);
                }
                return result;
            }
            catch (Exception e)
            {
                _trace.StopActivity(activity, e);
                throw;
            }
            finally
            {
                ConvertContext.Tracing = false;
            }
        }

        class InnerConvertContext : Dictionary<string, object>, IConvertContext
        {
            public IConvertSettings Settings { get; } = new InnerConvertSettings();

            public void Dispose() => Clear();
        }

        class InnerConvertSettings : IConvertSettings
        {

            public TraceListener Trace => null;

            public IEnumerable<ITranslator> Translators => Converts.Settings.Translators;

            public IStringSerializer StringSerializer => Converts.Settings.StringSerializer;

            public CultureInfo CultureInfo => Converts.Settings.CultureInfo;

            public NumberFormatInfo NumberFormatInfo => Converts.Settings.NumberFormatInfo;

            public Encoding Encoding => Converts.Settings.Encoding;

            public IConvertor<T1> GetConvertor<T1>(IConvertContext context) => Converts.Settings.GetConvertor<T1>(context);

            public Dictionary<Type, IFormatProvider> FormatProviders => Converts.Settings.FormatProviders;

            public StringSeparator StringSeparator => Converts.Settings.StringSeparator;

            public StringSplitOptions StringSplitOptions => Converts.Settings.StringSplitOptions;

            public Dictionary<Type, string> FormatStrings => Converts.Settings.FormatStrings;

            public IReflectCompiler ReflectCompiler => Converts.Settings.ReflectCompiler;

            public bool StrictEnum => Converts.Settings.StrictEnum;
        }

        private string GetDebuggerDisplay()
        {
            return $"Trace({InnerConvertor.GetType().GetFriendlyName()},out={typeof(T).GetFriendlyName()})";
        }
    }
}
